// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
//
// $Id: basic_logger.hpp 318 2009-03-02 14:03:21Z heavyzheng $
//

#ifndef ZZHENG_LOGGING_BASIC_LOGGER_HPP_20080901__
#define ZZHENG_LOGGING_BASIC_LOGGER_HPP_20080901__

#include <zzheng/logging/config.hpp>
#include <zzheng/logging/log_level.hpp>
#include <zzheng/logging/common.hpp>
#include <zzheng/logging/to_str.hpp>

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <string>
#include <typeinfo>
#include <exception>
#include <iterator>

namespace zzheng {
namespace logging {

    template<class Factory> class basic_log_factory; // forward declaration.

    template<class Factory>
    class basic_logger {

        typedef basic_logger<Factory>          this_type;
        typedef typename Factory::logger_type  impl_type;

        friend class basic_log_factory<Factory>;

    public:

        // Use auto-generated copy constructor.
        // Use auto-generated copy assignment.
        // Use auto-generated destructor.

        //! Returns the name of this logger.
        //! \return the name of this logger.
        const std::string& name() const {
            return name_;
        }

        //! Returns the direct child logger by name. If the child logger does not exist, this
        //! function will create a new one.
        //! \param name  the name of the direct child logger.
        //! \return the named direct child logger.
        this_type child(const std::string& name) {
            if (boost::shared_ptr<impl_type> tmp = pimpl_.lock()) {
                return this_type(tmp->child(name));
            } else {
                return this_type();
            }
        }

        //! Returns the threshold of this logger. A message written to this logger will be sent to
        //! the destination only if its level is equal to or greater than the threshold of this
        //! logger. Otherwise, the message will be ignored.
        //! \return the threshold of this logger.
        log_level threshold() const {
            if (boost::shared_ptr<impl_type> tmp = pimpl_.lock()) {
                return tmp->threshold();
            } else {
                return undefined_level();
            }
        }

        //! Checks if the specified log level is enabled (higher than the threshold).
        //! \param level  the log level to check.
        //! \return true if the log level is enabled, false otherwise.
        bool enabled(const log_level& level) const {
            return (level >= threshold());
        }

        //! Sets the threshold of this logger. The threshold will be propagated to all the direct
        //! and indirect child loggers.
        //! \param threshold  the threshold to set.
        void set_threshold(const log_level& threshold) {
            if (boost::shared_ptr<impl_type> tmp = pimpl_.lock()) {
                tmp->set_threshold(threshold);
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // functions to log values of different types
        //

        //! Logs a message to this logger at the specified log level.
        //! \param level  the log level of the message.
        //! \param msg    the message to log.
        void log(const log_level& level, const std::string& msg) {
            if (boost::shared_ptr<impl_type> tmp = pimpl_.lock()) {
                tmp->log(level, msg);
            }
        }

        //! Logs an exception to this logger at the specified log level.
        //!
        //! Note: we should have overloaded the log() function on std::string and std::exception,
        //! if Visual C++ (7.1 and 8.0) did not provide std::exception with a non-explicit
        //! constructor taking a const char*, which is not standard compliant. (I wish one day
        //! Microsoft would pay for its stupidity.)
        //!
        //! \param level  the log level of the exception.
        //! \param ex     the exception to log.
        void log_ex(const log_level& level, const std::exception& ex) {
            log(level, detail::exception_to_str(ex));
        }

        //! Logs all the values in the range specified by the begin position and the end position.
        //! It is required that value_type of the Iterator can be printed to an output stream, or,
        //! if value_type of the Iterator is std::pair, both first_type and second_type can be
        //! printed to an output stream.
        //! \tparam Iterator  the iterator type of the range.
        //! \param  level     the log level of the range.
        //! \param  msg       the message to log before the values in the range.
        //! \param  begin     the begin position of the range to log.
        //! \param  end       the end position of the range to log.
        template<typename Iterator>
        void log_range(const log_level& level,
                       const std::string& msg,
                       Iterator begin,
                       Iterator end) {
            if (boost::shared_ptr<impl_type> tmp = pimpl_.lock()) {
                typedef typename std::iterator_traits<Iterator>::value_type value_type;
                std::string str = msg + " : [";
                for (Iterator i = begin; i != end; ++i) {
                    detail::value_to_str<value_type> to_str;
                    str.append(to_str(*i)).append(",");
                }
                str.append("]");
                tmp->log(level, str);
            }
        }

        //! Logs the value type and the address of the specified pointer.
        //! \param level  the log level of the address.
        //! \param msg    the message to log before the pointer.
        //! \param ptr    the pointer to log.
        template<typename T>
        void log_ptr(const log_level& level, const std::string& msg, T* ptr) {
            if (boost::shared_ptr<impl_type> tmp = pimpl_.lock()) {
                std::string str = msg + " : " + detail::ptr_to_str(ptr);
                tmp->log(level, str);
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // helper functions to check threshold: delegating to enabled()
        //

        //! Checks if the trace level is enabled.
        //! \return true if the trace level is enabled, false otherwise.
        bool trace_enabled() const {
            return enabled(trace_level());
        }

        //! Checks if the debug level is enabled.
        //! \return true if the debug level is enabled, false otherwise.
        bool debug_enabled() const {
            return enabled(debug_level());
        }

        //! Checks if the info level is enabled.
        //! \return true if the info level is enabled, false otherwise.
        bool info_enabled() const {
            return enabled(info_level());
        }

        //! Checks if the warn level is enabled.
        //! \return true if the warn level is enabled, false otherwise.
        bool warn_enabled() const {
            return enabled(warn_level());
        }

        //! Checks if the error level is enabled.
        //! \return true if the error level is enabled, false otherwise.
        bool error_enabled() const {
            return enabled(error_level());
        }

        //! Checks if the fatal level is enabled.
        //! \return true if the fatal level is enabled, false otherwise.
        bool fatal_enabled() const {
            return enabled(fatal_level());
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // helper functions to log string messages: delegating to log()
        //

        //! Logs a message at the trace level, same as log(log_level::trace_level, msg).
        //! \param msg  the message to log.
        void trace(const std::string& msg) {
            log(trace_level(), msg);
        }

        //! Logs a message at the debug level, same as log(log_level::debug_level, msg).
        //! \param msg  the message to log.
        void debug(const std::string& msg) {
            log(debug_level(), msg);
        }

        //! Logs a message at the info level, same as log(log_level::info_level, msg).
        //! \param msg  the message to log.
        void info(const std::string& msg) {
            log(info_level(), msg);
        }

        //! Logs a message at the warn level, same as log(log_level::warn_level, msg).
        //! \param msg  the message to log.
        void warn(const std::string& msg) {
            log(warn_level(), msg);
        }

        //! Logs a message at the error level, same as log(log_level::error_level, msg).
        //! \param msg  the message to log.
        void error(const std::string& msg) {
            log(error_level(), msg);
        }

        //! Logs a message at the fatal level, same as log(log_level::fatal_level, msg).
        //! \param msg  the message to log.
        void fatal(const std::string& msg) {
            log(fatal_level(), msg);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // helper functions to log exceptions: delegating to log_ex()
        //

        //! Logs an exception at the trace level, same as log(log_level::trace_level, ex).
        //! \param ex  the exception to log.
        void trace_ex(const std::exception& ex) {
            log_ex(trace_level(), ex);
        }

        //! Logs an exception at the debug level, same as log(log_level::debug_level, ex).
        //! \param ex  the exception to log.
        //! \return the exception to log, same as ex.
        void debug_ex(const std::exception& ex) {
            log_ex(debug_level(), ex);
        }

        //! Logs an exception at the info level, same as log(log_level::info_level, ex).
        //! \param ex  the exception to log.
        //! \return the exception to log, same as ex.
        void info_ex(const std::exception& ex) {
            log_ex(info_level(), ex);
        }

        //! Logs an exception at the warn level, same as log(log_level::warn_level, ex).
        //! \param ex  the exception to log.
        //! \return the exception to log, same as ex.
        void warn_ex(const std::exception& ex) {
            log_ex(warn_level(), ex);
        }

        //! Logs an exception at the error level, same as log(log_level::error_level, ex).
        //! \param ex  the exception to log.
        //! \return the exception to log, same as ex.
        void error_ex(const std::exception& ex) {
            log_ex(error_level(), ex);
        }

        //! Logs an exception at the fatal level, same as log(log_level::fatal_level, ex).
        //! \param ex  the exception to log.
        //! \return the exception to log, same as ex.
        void fatal_ex(const std::exception& ex) {
            log_ex(fatal_level(), ex);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // helper functions to log ranges: delegating to log_range()
        //

        //! Logs a range of values at the trace level.
        //! \todo TODO: documents.
        template<typename Iterator>
        void trace_range(const std::string& msg, Iterator begin, Iterator end) {
            log_range(trace_level(), msg, begin, end);
        }

        //! Logs a range of values at the debug level.
        //! \todo TODO: documents.
        template<typename Iterator>
        void debug_range(const std::string& msg, Iterator begin, Iterator end) {
            log_range(debug_level(), msg, begin, end);
        }

        //! Logs a range of values at the info level.
        //! \todo TODO: documents.
        template<typename Iterator>
        void info_range(const std::string& msg, Iterator begin, Iterator end) {
            log_range(info_level(), msg, begin, end);
        }

        //! Logs a range of values at the warn level.
        //! \todo TODO: documents.
        template<typename Iterator>
        void warn_range(const std::string& msg, Iterator begin, Iterator end) {
            log_range(warn_level(), msg, begin, end);
        }

        //! Logs a range of values at the error level.
        //! \todo TODO: documents.
        template<typename Iterator>
        void error_range(const std::string& msg, Iterator begin, Iterator end) {
            log_range(error_level(), msg, begin, end);
        }

        //! Logs a range of values at the fatal level.
        //! \todo TODO: documents.
        template<typename Iterator>
        void fatal_range(const std::string& msg, Iterator begin, Iterator end) {
            log_range(fatal_level(), msg, begin, end);
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // helper functions to log pointers: delegating to log_ptr()
        //

        //! Logs a pointer at the trace level.
        template<typename T>
        void trace_ptr(const std::string& msg, T* ptr) {
            log_ptr(trace_level(), msg, ptr);
        }

        //! Logs a pointer at the debug level.
        template<typename T>
        void debug_ptr(const std::string& msg, T* ptr) {
            log_ptr(debug_level(), msg, ptr);
        }

        //! Logs a pointer at the info level.
        template<typename T>
        void info_ptr(const std::string& msg, T* ptr) {
            log_ptr(info_level(), msg, ptr);
        }

        //! Logs a pointer at the warn level.
        template<typename T>
        void warn_ptr(const std::string& msg, T* ptr) {
            log_ptr(warn_level(), msg, ptr);
        }

        //! Logs a pointer at the error level.
        template<typename T>
        void error_ptr(const std::string& msg, T* ptr) {
            log_ptr(error_level(), msg, ptr);
        }

        //! Logs a pointer at the fatal level.
        template<typename T>
        void fatal_ptr(const std::string& msg, T* ptr) {
            log_ptr(fatal_level(), msg, ptr);
        }

    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private constructors
        //

        //! Private default constructor.
        explicit basic_logger(): pimpl_(), name_() {
            // Do nothing.
        }

        //! Private constructor.
        explicit basic_logger(const boost::weak_ptr<impl_type>& pimpl): pimpl_(pimpl), name_() {
            if (boost::shared_ptr<impl_type> tmp = pimpl_.lock()) {
                name_ = tmp->name();
            }
        }

    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private member variables
        //

        boost::weak_ptr<impl_type> pimpl_; //!< Weak pointer to the underlying logger object.
        std::string                name_;  //!< The name of this logger.

    }; // class basic_logger<Factory>

} // namespace zzheng::logging
} // namespace zzheng

#endif // ZZHENG_LOGGING_BASIC_LOGGER_HPP_20080901__



